/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2010 huliqing, huliqing.cn@gmail.com
 *
 * This file is part of QFaces.
 * QFaces is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * QFaces is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with QFaces.  If not, see <http://www.gnu.org/licenses/>.
 *
 * 这个文件是QFaces的一部分。
 * 您可以单独使用或分发这个文件，但请不要移除这个头部声明信息.
 * QFaces是一个自由软件，您可以自由分发、修改其中的源代码或者重新发布它，
 * 新的任何修改后的重新发布版必须同样在遵守LGPL3或更后续的版本协议下发布.
 * 关于LGPL协议的细则请参考COPYING、COPYING.LESSER文件，
 * 您可以在QFaces的相关目录中获得LGPL协议的副本，
 * 如果没有找到，请连接到 http://www.gnu.org/licenses/ 查看并获取副本。
 *
 * - Author: Huliqing
 * - Contact: huliqing.cn@gmail.com
 * - License: GNU Lesser General Public License (LGPL)
 * - Blog and source code availability: http://www.huliqing.name/
 */

package name.huliqing.qfaces.component;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import java.util.logging.Logger;
import javax.el.MethodExpression;
import javax.el.ValueExpression;
import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.convert.DateTimeConverter;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import name.huliqing.qfaces.QFaces;
import name.huliqing.qfaces.XmlOper;
import name.huliqing.qfaces.component.Frame.CssPosition;
import name.huliqing.qfaces.model.ActionDate;

public class Calendar extends UIInput {
	private final static Logger logger = Logger.getLogger(Calendar.class.getName());
	
	// As calendar, 绑定后台方法,该方法向后台传递年份及月分，并获取这个月分存在的活动日期列表，
	// 返回类型为List<ActionDate>，其中包含了这个月份所有存在活动事件的日期及事件描述
	private MethodExpression listener;
	
	// As calendar, 日历的链接地址，当运行方式为“非POP”时，如果某个日期存在“活动”则
	// 点击某一个日期时需要连接到目标页面。href即为连接地址。
	private String href; 
	
	// As calendar, href连接地址的打开方式，如“_blank”,"_self", "_parent"等
	private String target;
	
	// As calendar, 是否显示“活动事件”面板，当某一天存在活动事件时，鼠标移动到日期上面会弹出该窗口
	private Boolean showActionPanel;
	
	// As pop, 渲染为“弹出窗口”模式，这个模式将作为一般的日期输入组件来用
	private Boolean renderAsPop;
	
	// As pop, 
	// 参数For, 该参数指定到目标客户端组件ID，也就是将选择的日期赋值给for参数所
	// 指定的目标组件，格式如下 "targetId1,targetId2,targetId3" 用半角逗号分隔
	// 这样可以将日期值赋给多个目标组件，并且需要能够指定到一般的HTML客户端组件
	// 这个参数只有在renderAsPop属性为true的时候才有意义
	private String _for;
	
	// As pop, 图片URL：Open Button的图片URL
	private String imgFlagSrc;
	
	// As pop, 日期格式，如："yyyy-MM-dd"
	private String pattern;
	
	// As pop, value属性所绑定的日期的时区,如：GMT+8
	private String timeZone;
	
	// As pop, InputField是否渲染为“只读”
	private Boolean readOnly;
	
	// As pop, 是否自动关闭，即用户选择某个日期之后关闭日历组件
	private Boolean autoClose;
	
	// "月分描述", 如：“一月,二月,三月,,,”， 用半角逗号分隔
	private String strMonth;
	
	// “一周描述”， 如：“日,一,二,三,四,五,六”, 用半角逗号分隔
	private String strWeek;
	
	// 图片URL：“上一个月”
	private String imgPreviousSrc;
	
	// 图片URL：“下一个月”
	private String imgNextSrc;
	
	// 图片URL：“下拉框箭头”,用于弹出框选择年份，月份,
	private String imgSelectSrc;
	
	// 起始年份,首次打开窗口时，年份选择列表的默认起始年份,注：该值只有在目标组件的值为空时，才有效。
	// 如果目标组件的值不为空，则以目标组件的年份值- visibleYear / 2 作为真正的startYear,这样保证
	// 年份选择列表的目标组件年份值始终处于列表的正中
	private Integer startYear;
	
	// 年份选择列表的可见个数
	private Integer visibleYear;
	
	// 日期组件的样式类
	// input组件的样式(这个input只有在绑定了value之后才会出现)
	private String style;
	
	// input组件的样式类（同上）
	private String styleClass;
	
	// 组件日期区域的样式（不包含年份,月份）,该区域内含一个table,table中即为存放一个月中的1-31天的日期
	private String styleClassCalendarBody;
	
	// Footer部分的样式类（注：不是Today区域的样式类，
	// 但是Today区域被包含于Footer区域，所以这个样式同样会影响Today区域）
	// 注：这个样式类不会通过js传递到客户端
	private String styleClassCalendarFooter;
	
	// 当前选中的年份样式
	private String styleClassYearSelected;
	
	// 当前选中的月份样式
	private String styleClassMonthSelected;
	
	// 当前选中的日期样式
	private String styleClassDaySelected;
	
	// 当前的日期样式
	private String styleClassDayCurrent;
	
	// 鼠标放上时，日期的响应样式
	private String styleClassDayOnmouseover;
	
	// 显示的年份的样式
	private String styleClassYear;
	
	// 显示的月份的样式
	private String styleClassMonth;
	
	// 显示的星期的样式
	private String styleClassWeek;
	
	// 日期的样式
	private String styleClassDayOfMonth;
	
	// “活动事件描述面板”的样式类
	private String styleClassActionPanel;
	
	// “活动事件日期”的样式类
	private String styleClassActionDate;
	
	// 窗口类
	private Frame frame;
	
	// lazy/energy
	private String loadType;
	
	public Calendar() {
		super();
	}

	private Object[] _values;
	@Override
	public void restoreState(FacesContext fc, Object state) {
		_values = (Object[]) state; 
		super.restoreState(fc, _values[0]);
		this.listener = (MethodExpression) _values[1];
		this.href = (String) _values[2];
		this.target = (String) _values[3];
		this.showActionPanel = (Boolean) _values[4];
		this.renderAsPop = (Boolean) _values[5];
		this._for = (String) _values[6];
		this.imgFlagSrc = (String) _values[7];
		this.pattern = (String) _values[8];
		this.timeZone = (String) _values[9];
		this.readOnly = (Boolean) _values[10];
		this.autoClose = (Boolean) _values[11];
		this.strMonth = (String) _values[12];
		this.strWeek = (String) _values[13];
		this.imgPreviousSrc = (String) _values[14];
		this.imgNextSrc = (String) _values[15];
		this.imgSelectSrc = (String) _values[16];
		this.startYear = (Integer) _values[17];
		this.visibleYear = (Integer) _values[18];
		this.style = (String) _values[19];
		this.styleClass = (String) _values[20];
		this.styleClassCalendarBody = (String) _values[21];
		this.styleClassCalendarFooter = (String) _values[22];
		this.styleClassYearSelected = (String) _values[23];
		this.styleClassMonthSelected = (String) _values[24];
		this.styleClassDaySelected = (String) _values[25];
		this.styleClassDayCurrent = (String) _values[26];
		this.styleClassDayOnmouseover = (String) _values[27];
		this.styleClassYear = (String) _values[28];
		this.styleClassMonth = (String) _values[29];
		this.styleClassWeek = (String) _values[30];
		this.styleClassDayOfMonth = (String) _values[31];
		this.styleClassActionPanel = (String) _values[32];
		this.styleClassActionDate = (String) _values[33];
		this.frame = (Frame) _values[34];
		this.loadType = (String) _values[35];
	}
	
	@Override
	public Object saveState(FacesContext fc) {
		if (_values == null)
			_values = new Object[36];
		_values[0] = super.saveState(fc);
		_values[1] = this.listener;
		_values[2] = this.href;
		_values[3] = this.target;
		_values[4] = this.showActionPanel;
		_values[5] = this.renderAsPop;
		_values[6] = this._for;
		_values[7] = this.imgFlagSrc;
		_values[8] = this.pattern;
		_values[9] = this.timeZone;
		_values[10] = this.readOnly;
		_values[11] = this.autoClose;
		_values[12] = this.strMonth;
		_values[13] = this.strWeek;
		_values[14] = this.imgPreviousSrc;
		_values[15] = this.imgNextSrc;
		_values[16] = this.imgSelectSrc;
		_values[17] = this.startYear;
		_values[18] = this.visibleYear;
		_values[19] = this.style;
		_values[20] = this.styleClass;
		_values[21] = this.styleClassCalendarBody;
		_values[22] = this.styleClassCalendarFooter;
		_values[23] = this.styleClassYearSelected;
		_values[24] = this.styleClassMonthSelected;
		_values[25] = this.styleClassDaySelected;
		_values[26] = this.styleClassDayCurrent;
		_values[27] = this.styleClassDayOnmouseover;
		_values[28] = this.styleClassYear;
		_values[29] = this.styleClassMonth;
		_values[30] = this.styleClassWeek;
		_values[31] = this.styleClassDayOfMonth;
		_values[32] = this.styleClassActionPanel;
		_values[33] = this.styleClassActionDate;
		_values[34] = this.frame;
		_values[35] = this.loadType;
		return _values;
	}
	
	private Object getFromExp(String name) {
		ValueExpression _ve = getValueExpression(name);
        if (_ve != null)
            return _ve.getValue(getFacesContext().getELContext());
        else
            return null;
	} 
	
	// ---- Getter
	
	public MethodExpression getListener() {
		return this.listener;
	}
	
	public void setListener(MethodExpression listener) {
		this.listener = listener;
	}
	
	public void setListenerAsExpression(String methodExpression) {
		MethodExpression me = QFaces.createMethodExpression(
				methodExpression, 
				List.class, 
				new Class[]{Integer.class, Integer.class});
		setListener(me);
	}
	
	public String getFor() {
		if (this._for != null)
			return this._for;
		return (String) this.getFromExp("for");
	}

	public String getHref() {
		if (this.href != null)
			return this.href;
		return (String) this.getFromExp("href");
	}

	public String getTarget() {
		if (this.target != null)
			return this.target;
		return (String) this.getFromExp("target");
	}

	public Boolean getShowActionPanel() {
		if (this.showActionPanel != null) {
			return this.showActionPanel;
		}
		return (Boolean) this.getFromExp("showActionPanel");
	}

	public Boolean getRenderAsPop() {
		if (this.renderAsPop != null) {
			return this.renderAsPop;
		}
		return (Boolean) this.getFromExp("renderAsPop");
	}

	public String getImgFlagSrc() {
		if (this.imgFlagSrc != null) {
			return this.imgFlagSrc;
		}
		return (String) this.getFromExp("imgFlagSrc");
	}

	public String getPattern() {
		if (this.pattern != null) {
			return this.pattern;
		}
		return (String) this.getFromExp("pattern");
	}

	public String getTimeZone() {
		if (this.timeZone != null) {
			return this.timeZone;
		}
		return (String) this.getFromExp("timeZone");
	}

	public Boolean getReadOnly() {
		if (this.readOnly != null) {
			return this.readOnly;
		}
		return (Boolean) this.getFromExp("readOnly");
	}

	public Boolean getAutoClose() {
		if (this.autoClose != null) {
			return this.autoClose;
		}
		return (Boolean) this.getFromExp("autoClose");
	}

	public String getStrMonth() {
		if (this.strMonth != null) {
			return this.strMonth;
		}
		return (String) this.getFromExp("strMonth");
	}

	public String getStrWeek() {
		if (this.strWeek != null) {
			return this.strWeek;
		}
		return (String) this.getFromExp("strWeek");
	}

	public String getImgPreviousSrc() {
		if (this.imgPreviousSrc != null) {
			return this.imgPreviousSrc;
		}
		return (String) this.getFromExp("imgPreviousSrc");
	}

	public String getImgNextSrc() {
		if (this.imgNextSrc != null) {
			return this.imgNextSrc;
		}
		return (String) this.getFromExp("imgNextSrc");
	}

	public String getImgSelectSrc() {
		if (this.imgSelectSrc != null) {
			return this.imgSelectSrc;
		}
		return (String) this.getFromExp("imgSelectSrc");
	}

	public Integer getStartYear() {
		if (this.startYear != null) {
			return this.startYear;
		}
		return (Integer) this.getFromExp("startYear");
	}

	public Integer getVisibleYear() {
		if (this.visibleYear != null) {
			return this.visibleYear;
		}
		return (Integer) this.getFromExp("visibleYear");
	}

	public String getStyle() {
		if (this.style != null) {
			return this.style;
		}
		return (String) this.getFromExp("style");
	}

	public String getStyleClass() {
		if (this.styleClass != null) {
			return this.styleClass;
		}
		return (String) this.getFromExp("styleClass");
	}

	public String getStyleClassCalendarBody() {
		if (this.styleClassCalendarBody != null) {
			return this.styleClassCalendarBody;
		}
		return (String) this.getFromExp("styleClassCalendarBody");
	}

	public String getStyleClassCalendarFooter() {
		if (this.styleClassCalendarFooter != null) {
			return this.styleClassCalendarFooter;
		}
		return (String) this.getFromExp("styleClassCalendarFooter");
	}

	public String getStyleClassYearSelected() {
		if (this.styleClassYearSelected != null) {
			return this.styleClassYearSelected;
		}
		return (String) this.getFromExp("styleClassYearSelected");
	}

	public String getStyleClassMonthSelected() {
		if (this.styleClassMonthSelected != null) {
			return this.styleClassMonthSelected;
		}
		return (String) this.getFromExp("styleClassMonthSelected");
	}

	public String getStyleClassDaySelected() {
		if (this.styleClassDaySelected != null) {
			return this.styleClassDaySelected;
		}
		return (String) this.getFromExp("styleClassDaySelected");
	}

	public String getStyleClassDayCurrent() {
		if (this.styleClassDayCurrent != null) {
			return this.styleClassDayCurrent;
		}
		return (String) this.getFromExp("styleClassDayCurrent");
	}

	public String getStyleClassDayOnmouseover() {
		if (this.styleClassDayOnmouseover != null) {
			return this.styleClassDayOnmouseover;
		}
		return (String) this.getFromExp("styleClassDayOnmouseover");
	}

	public String getStyleClassYear() {
		if (this.styleClassYear != null) {
			return this.styleClassYear;
		}
		return (String) this.getFromExp("styleClassYear");
	}

	public String getStyleClassMonth() {
		if (this.styleClassMonth != null) {
			return this.styleClassMonth;
		}
		return (String) this.getFromExp("styleClassMonth");
	}

	public String getStyleClassWeek() {
		if (this.styleClassWeek != null) {
			return this.styleClassWeek;
		}
		return (String) this.getFromExp("styleClassWeek");
	}

	public String getStyleClassDayOfMonth() {
		if (this.styleClassDayOfMonth != null) {
			return this.styleClassDayOfMonth;
		}
		return (String) this.getFromExp("styleClassDayOfMonth");
	}

	public String getStyleClassActionPanel() {
		if (this.styleClassActionPanel != null) {
			return this.styleClassActionPanel;
		}
		return (String) this.getFromExp("styleClassActionPanel");
	}

	public String getStyleClassActionDate() {
		if (this.styleClassActionDate != null) {
			return this.styleClassActionDate;
		}
		return (String) this.getFromExp("styleClassActionDate");
	}

	public Frame getFrame() {
		return frame;
	}
	
	public String getLoadType() {
		if (loadType != null)
			return loadType;
		return (String) this.getFromExp("loadType");
	}
	
	// ---- Setter
	
	public void setFor(String _for) {
		this._for = _for;
	}

	public void setHref(String href) {
		this.href = href;
	}

	public void setTarget(String target) {
		this.target = target;
	}

	public void setShowActionPanel(Boolean showActionPanel) {
		this.showActionPanel = showActionPanel;
	}

	public void setRenderAsPop(Boolean renderAsPop) {
		this.renderAsPop = renderAsPop;
	}

	public void setImgFlagSrc(String imgFlagSrc) {
		this.imgFlagSrc = imgFlagSrc;
	}

	public void setPattern(String pattern) {
		this.pattern = pattern;
	}

	public void setTimeZone(String timeZone) {
		this.timeZone = timeZone;
	}

	public void setReadOnly(Boolean readOnly) {
		this.readOnly = readOnly;
	}

	public void setAutoClose(Boolean autoClose) {
		this.autoClose = autoClose;
	}

	public void setStrMonth(String strMonth) {
		this.strMonth = strMonth;
	}

	public void setStrWeek(String strWeek) {
		this.strWeek = strWeek;
	}

	public void setImgPreviousSrc(String imgPreviousSrc) {
		this.imgPreviousSrc = imgPreviousSrc;
	}

	public void setImgNextSrc(String imgNextSrc) {
		this.imgNextSrc = imgNextSrc;
	}

	public void setImgSelectSrc(String imgSelectSrc) {
		this.imgSelectSrc = imgSelectSrc;
	}

	public void setStartYear(Integer startYear) {
		this.startYear = startYear;
	}

	public void setVisibleYear(Integer visibleYear) {
		this.visibleYear = visibleYear;
	}

	public void setStyle(String style) {
		this.style = style;
	}

	public void setStyleClass(String styleClass) {
		this.styleClass = styleClass;
	}

	public void setStyleClassCalendarBody(String styleClassCalendarBody) {
		this.styleClassCalendarBody = styleClassCalendarBody;
	}

	public void setStyleClassCalendarFooter(String styleClassCalendarFooter) {
		this.styleClassCalendarFooter = styleClassCalendarFooter;
	}

	public void setStyleClassYearSelected(String styleClassYearSelected) {
		this.styleClassYearSelected = styleClassYearSelected;
	}

	public void setStyleClassMonthSelected(String styleClassMonthSelected) {
		this.styleClassMonthSelected = styleClassMonthSelected;
	}

	public void setStyleClassDaySelected(String styleClassDaySelected) {
		this.styleClassDaySelected = styleClassDaySelected;
	}

	public void setStyleClassDayCurrent(String styleClassDayCurrent) {
		this.styleClassDayCurrent = styleClassDayCurrent;
	}

	public void setStyleClassDayOnmouseover(String styleClassDayOnmouseover) {
		this.styleClassDayOnmouseover = styleClassDayOnmouseover;
	}

	public void setStyleClassYear(String styleClassYear) {
		this.styleClassYear = styleClassYear;
	}

	public void setStyleClassMonth(String styleClassMonth) {
		this.styleClassMonth = styleClassMonth;
	}

	public void setStyleClassWeek(String styleClassWeek) {
		this.styleClassWeek = styleClassWeek;
	}

	public void setStyleClassDayOfMonth(String styleClassDayOfMonth) {
		this.styleClassDayOfMonth = styleClassDayOfMonth;
	}

	public void setStyleClassActionPanel(String styleClassActionPanel) {
		this.styleClassActionPanel = styleClassActionPanel;
	}

	public void setStyleClassActionDate(String styleClassActionDate) {
		this.styleClassActionDate = styleClassActionDate;
	}

	public void setFrame(Frame frame) {
		this.frame = frame;
	}
	
	public void setLoadType(String loadType) {
		this.loadType = loadType;
	}
	
	
	// ---- Encode 
	
	@Override
	public void encodeBegin(FacesContext fc) throws IOException {
//		super.encodeBegin(fc);
		
		// 载入资源
        QFaces.getResLoader2().loadJavaScript(this, "QFaces-DateFormat.js");
        QFaces.getResLoader2().loadJavaScript(this, "Calendar.js");
        
        int currentYear = java.util.Calendar.getInstance().get(java.util.Calendar.YEAR);
        
        if (getHref() == null)
        	setHref("");
        
        if (getTarget() == null)
        	setTarget("_self");
        
        if (getShowActionPanel() == null)
        	setShowActionPanel(Boolean.FALSE);
        
        if (getRenderAsPop() == null)
        	setRenderAsPop(Boolean.FALSE);
        
        if (getFor() == null)
        	setFor("");
        
        if (getImgFlagSrc() == null)
        	setImgFlagSrc(QFaces.getResLoader2().makeImageSrc("04-flag.png"));
        
        if (getPattern() == null)
        	setPattern("yyyy-MM-dd");
        
        if (getTimeZone() == null)
        	setTimeZone(TimeZone.getDefault().getID());
        
        if (getReadOnly() == null)
        	setReadOnly(Boolean.FALSE);
        
        if (getAutoClose() == null)
        	setAutoClose(Boolean.TRUE);
        
        if (getVisibleYear() == null)
        	setVisibleYear(12);
        
        if (getStartYear() == null) 
        	setStartYear(currentYear - getVisibleYear() / 2);
        
        if (getStrMonth() == null) 
        	setStrMonth("一月,二月,三月,四月,五月,六月,七月,八月,九月,十月,十一月,十二月");
        
        if (getStrWeek() == null)
        	setStrWeek("日,一,二,三,四,五,六");
        
        if (getImgPreviousSrc() == null)
        	setImgPreviousSrc(QFaces.getResLoader2().makeImageSrc("04-previous.png"));
        
        if (getImgNextSrc() == null)
        	setImgNextSrc(QFaces.getResLoader2().makeImageSrc("04-next.png"));
        
        if (getImgSelectSrc() == null)
        	setImgSelectSrc(QFaces.getResLoader2().makeImageSrc("04-select.png"));
        
        if (getStyle() == null)
        	setStyle("");
        
        if (getStyleClass() == null)
        	setStyleClass("_qf_04_style");
        
        if (getStyleClassCalendarBody() == null)
        	setStyleClassCalendarBody("");
		
        if (getStyleClassCalendarFooter() == null)
        	setStyleClassCalendarFooter("_qf_04_footer");
        
        if (getStyleClassYearSelected() == null)
        	setStyleClassYearSelected("_qf_04_yearSelected");
        
        if (getStyleClassMonthSelected() == null)
        	setStyleClassMonthSelected("_qf_04_monthSelected");
        
        if (getStyleClassDaySelected() == null)
        	setStyleClassDaySelected("_qf_04_daySelected");
        
        if (getStyleClassDayCurrent() == null)
        	setStyleClassDayCurrent("_qf_04_dayCurrent");
        
        if (getStyleClassDayOnmouseover() == null)
        	setStyleClassDayOnmouseover("_qf_04_dayOnmouseover");
        	
        if (getStyleClassYear() == null)
        	setStyleClassYear("_qf_04_year");
        
        if (getStyleClassMonth() == null)
        	setStyleClassMonth("_qf_04_month");
        
        if (getStyleClassWeek() == null)
        	setStyleClassWeek("_qf_04_week");
        	
        if (getStyleClassDayOfMonth() == null)
        	setStyleClassDayOfMonth("_qf_04_dayOfMonth");
        
        if (getStyleClassActionPanel() == null)
        	setStyleClassActionPanel("_qf_04_actionPanel");
        	
        if (getStyleClassActionDate() == null)
        	setStyleClassActionDate("_qf_04_actionDate");
        
        if (getFrame() == null) {
        	setFrame(new Frame());
        	getFrame().loadAttributeFrom(this);
        }
		
        
    	// 组件ID
    	String ID_CLIENT = getClientId(fc);
    	
    	// 保存日期值的Field
    	String ID_INPUT = ID_CLIENT;
    	
    	// 整个渲染区域的最外框ID,与ID_CLIENT不同，ID_CLIENT是作为组件的唯一标识
    	String ID_FULL = ID_CLIENT + ":full";
    	
    	// 日历区域的ID（含header及body,footer）
    	String ID_CONTENT = ID_CLIENT + ":content";
    	
    	// 日历区域的header ID (含年，月)
    	String ID_CONTENT_HEADER = ID_CONTENT + ":header";
    	
    	// 日历区域的body ID （含日历,一个月中的所有天数）
    	String ID_CONTENT_BODY = ID_CONTENT + ":body";
    	
    	// 日历区域的footer ID
    	String ID_CONTENT_FOOTER = ID_CONTENT + ":footer";
		

		// 组件的不同状态要求的默认属性不一样，但是仍然保留以用户的设置优先
		if (getRenderAsPop()) {
			if (getFrame().getVisible() == null)
				getFrame().setVisible(Boolean.FALSE);
			if (getFrame().getCssPosition() == null)
				getFrame().setCssPosition(CssPosition.ABSOLUTE.name());
		} else {
			if (getFrame().getDrag() == null)
				getFrame().setDrag(Boolean.FALSE);
			if (getFrame().getRenderCloseButton() == null)
				getFrame().setRenderCloseButton(Boolean.FALSE);
		}
		
		// 处理客户端长ID, 将组件所引用的ID（short id），转化为客户端的长ID类型，以便客户端
		if (!"".equals(getFor())) {
			String[] forIds = null;
			if (getFor().indexOf(",") != -1) {
				forIds = getFor().split(",");
			} else {
				forIds = new String[]{getFor()};
			}
			StringBuilder tempFor = new StringBuilder("");
			for (String forId : forIds) {
				UIComponent forCom = findComponent(forId.trim());
				if (forCom != null) {
					tempFor.append(forCom.getClientId(fc)).append(",");	// for 到JSF组件
				} else {
					tempFor.append(forId.trim()).append(","); // for 到一般的Html组件 
				}
			}
			setFor(tempFor.toString());
		}
		if (getFor().endsWith(",")) 
			setFor(getFor().substring(0, getFor().length() - 1));
		
		// 处理转换
		if (getConverter() == null) {
			DateTimeConverter con = new DateTimeConverter();
			con.setPattern(getPattern());
			con.setTimeZone(TimeZone.getTimeZone(getTimeZone()));
			setConverter(con);
		}
		
		// 检测参数是否正确
		
		if (!getRenderAsPop()) {
			if (getListener() == null) 
				logger.warning("Attribute \"listener\" not found as render " +
						"not a \"Date Selector\", 提示：作为日期选择器时，你应该设置组件属性renderAsPop=\"true\", " +
						"如果作为事件日历来使用，那么你应该给组件绑定\"listener\"属性.");
		}
		
		String strValue = getConverter().getAsString(fc, this, getValue());
		
		// ---- 开始渲染输出 
		
		ResponseWriter rw = fc.getResponseWriter();
		rw.startElement("span", this);
		rw.writeAttribute("id", ID_FULL, null);
		rw.writeAttribute("style", "border:0;", null);
		
		// 只有组件直接绑定了值,并且As pop(弹窗方式)的时候才有必要渲染input输入框
		if (getValueExpression("value") != null && getRenderAsPop()) {
			rw.startElement("input", this);
			rw.writeAttribute("id", ID_INPUT, null);
			rw.writeAttribute("name", ID_INPUT, null);	// 必须加\"name\"属性，否则fc.getExternalContext().getRequestParameterMap().get(inputId);将获取不到值
			rw.writeAttribute("type", "text", null);
			rw.writeAttribute("style", getStyle(), null);
			rw.writeAttribute("class", getStyleClass(), null);
			if (getReadOnly()) rw.writeAttribute("readOnly", "true", null);
			rw.writeAttribute("value", strValue, null);
			rw.endElement("input");
		}
		
		// 只有As pop(弹窗方式) 的时候才需要“开关”按钮
		if (getRenderAsPop()) {
			String event = "Q.F.UICalendar.display('" + ID_CLIENT + "')";
			rw.startElement("img", this);
			rw.writeAttribute("onclick", event, null);
			rw.writeAttribute("style", "cursor:pointer;vertical-align:middle;margin:0 0 1px 0;", null);
			rw.writeAttribute("src", getImgFlagSrc(), null); 
			rw.writeAttribute("alt", "", null);
			rw.endElement("img");
		}
		// 窗口
		getFrame().encodeBegin(fc);
		rw.startElement("table", this);
		rw.writeAttribute("border", "0", null);
		rw.writeAttribute("width", "100%", null);
		rw.writeAttribute("cellspacing", "0", null);
		rw.writeAttribute("cellpadding", "0", null);
			rw.startElement("tr", this);
				rw.startElement("td", this);
				rw.writeAttribute("id", ID_CONTENT_HEADER, null);
				rw.endElement("td");
			rw.endElement("tr");
			rw.startElement("tr", this);
				rw.startElement("td", this);
				rw.writeAttribute("id", ID_CONTENT_BODY, null);
				rw.endElement("td");
			rw.endElement("tr");
			rw.startElement("tr", this);
				rw.startElement("td", this);
				rw.writeAttribute("class", getStyleClassCalendarFooter(), null);
				rw.writeAttribute("id", ID_CONTENT_FOOTER, null);
				rw.endElement("td");
			rw.endElement("tr");
		rw.endElement("table");
		getFrame().encodeEnd(fc);
		
		rw.endElement("span");
		
		
		// ---- init
        StringBuilder jsInit = new StringBuilder("Q.F.UICalendar.init(")
                .append(" \"" + ID_CLIENT + "\"")
                .append(",\"" + ID_FULL + "\"")
                .append(",\"" + ID_INPUT + "\"")
                .append(",\"" + ID_CONTENT + "\"")
                .append(",\"" + ID_CONTENT_HEADER + "\"")
                .append(",\"" + ID_CONTENT_BODY + "\"")
                .append(",\"" + ID_CONTENT_FOOTER + "\"")
                .append(",\"" + getFrame().getFrameId() + "\"")
                .append(",\"" + this.getClass().getName() + "\"")
                .append(",\"" + (getListener() != null ? getListener().getExpressionString() : "") + "\"")
                .append(",\"" + getHref() + "\"")
                .append(",\"" + getTarget() + "\"")
                .append(",\"" + getShowActionPanel() + "\"")
                .append(",\"" + getImgFlagSrc() + "\"")
                .append(",\"" + getImgPreviousSrc() + "\"")
                .append(",\"" + getImgNextSrc() + "\"")
                .append(",\"" + getImgSelectSrc() + "\"")
                .append(",\"" + getStrMonth() + "\"")
                .append(",\"" + getStrWeek() + "\"")
                .append(",\"" + getFor() + "\"")
                .append(",\"" + getRenderAsPop() + "\"")
                .append(",\"" + getPattern() + "\"")
                .append(",\"" + getAutoClose() + "\"")
                .append(",\"" + getStartYear() + "\"")
                .append(",\"" + getVisibleYear() + "\"")
                .append(",\"" + getStyleClassCalendarBody() + "\"")
                .append(",\"" + getStyleClassYearSelected() + "\"")
                .append(",\"" + getStyleClassMonthSelected() + "\"")
                .append(",\"" + getStyleClassDaySelected() + "\"")
                .append(",\"" + getStyleClassDayCurrent() + "\"")
                .append(",\"" + getStyleClassDayOnmouseover() + "\"")
                .append(",\"" + getStyleClassYear() + "\"")
                .append(",\"" + getStyleClassMonth() + "\"")
                .append(",\"" + getStyleClassWeek() + "\"")
                .append(",\"" + getStyleClassDayOfMonth() + "\"")
                .append(",\"" + getStyleClassActionPanel() + "\"")
                .append(",\"" + getStyleClassActionDate() + "\"")
                .append(")");
        
        QFaces.getResLoader2().loadJavaScriptInit(this, jsInit.toString());
		
	}
	
	@Override
	public void encodeEnd(FacesContext fc) throws IOException {		
	}

	@Override
	public void decode(FacesContext fc) {
		super.decode(fc);
		// 只有绑定了值，才需要获取客户端的value来进行解码
		if (getValueExpression("value") != null) {
			String ID_INPUT = getClientId(fc); // ID必须与渲染到客户端时的ID一致
			String dateStr = fc.getExternalContext().getRequestParameterMap().get(ID_INPUT);
			setSubmittedValue(dateStr);
		}
	}
	
	// ---- Ajax
	
	public void ajax(Map<String, String> params, FacesContext fc) {
		logger.fine("ajax invoke on get...");
		// 获取有活动事件的日期列表
        String listenerExp = params.get("listenerExp");
        Integer year = Integer.parseInt(params.get("year"));
        Integer month = Integer.parseInt(params.get("month"));
        MethodExpression _listener = QFaces.createMethodExpression(listenerExp,
                        List.class,
                        new Class[]{Integer.class, Integer.class});
        Object[] args = {year, month};
        List<ActionDate> ads = (List<ActionDate>) _listener.invoke(fc.getELContext(), args);        
        try {
            Document doc = XmlOper.newDocument();
            Element nodeRoot = doc.createElement("root");
            doc.appendChild(nodeRoot);
            if (ads != null && !ads.isEmpty()) {
            	for (ActionDate ad : ads) {
        			Element dayElement = doc.createElement("day");
        			dayElement.setAttribute("dayOfMonth", ad.getDayOfMonth() != null ? ad.getDayOfMonth().toString() : "aa");
        			dayElement.setAttribute("description", ad.getDescription() != null ? ad.getDescription() :  "aa");
        			nodeRoot.appendChild(dayElement);
            	}
            }
            QFaces.getResponse().responseXML(fc, doc);
        } catch (ParserConfigurationException e) {
        	logger.warning(e.getMessage());
        }
	}
}
